/**
	require lib/jquery.js
	require common.js
	require permissionChecker.js
	require SDKDataManager.js
*/
if (typeof (QSAPI) == 'undefined') {
	var QSAPI = {};
}

if(!gData){
	var gData={};
}

QSAPI.TickersCache = {
	tickers : {},
	gKeyMap:{},
	add : function(ticker) {
		if (!this.tickers[ticker['symbol']]) {
			this.tickers[ticker['symbol']] = ticker;
		}
	},
	getByGKey:function(gKey){
		var srcTicker = this.gKeyMap[gKey];
		if (!srcTicker){
			return null;
		}
		if (srcTicker['isReady']){
			var targetTicker = new QSAPI.Ticker(srcTicker['symbol']);
			this._clone(srcTicker,targetTicker);
			return targetTicker;
		}
		return null;		
	},
	get : function(symbol) {
		var srcTicker = this.tickers[symbol];
		if (!srcTicker){
			return null;
		}
		if (srcTicker['isReady']){
			var targetTicker = new QSAPI.Ticker(symbol);
			this._clone(srcTicker,targetTicker);
			return targetTicker;
		}
		return null;
	},
	addGData:function(ticker){
		var gkey=ticker['gkey'];
		if (!gkey){
			return;
		}
		if (!gData[gkey]) {
			gData[gkey] = {};
		}
		var obj=$.extend(gData[gkey],ticker);
		delete obj.index;
		delete obj.getField;
		delete obj.setData;
		delete obj.setField;
		delete obj.etfTicker;
	},
	_clone:function (src,t){
		$.extend(true,t,src,{
			'index':t.index,
			'symbol':t.symbol
		});
	}
};

/**
 * @param symbolArray
 *            ["IBM","MSFT"]
 */
QSAPI.TickerFactory={
		create:function(symbolArray,callback){
			return new QSAPI.Tickers(symbolArray,callback);
		}
};

QSAPI.Tickers = function(symbolArray,callback) {	
	this.id = Util.IdProducer.getId();
	this.isAllGKey=false;
	this.callbackPool=[];
	callback=callback || {};
	if (!_checkParameter(symbolArray)) {
		_throwSymbolArrayError();
	}
	 	
	this.config = {
		groupSymbolQuantity : 30,
		idService :QSAPI.getQSPath()+ 'getids.jsp?symbol='
	};

	var config = this.config;
	var tickerList=null;
	var needResponseTimes = 0;
	var responsedTimes = 0;
	var cdm=QSAPI.DataManager;	
	this.isAllGKey=_checkAllAreGKey(symbolArray);
	
	if (this.isAllGKey){
		this.tickerList = _getTickersByGKeys(symbolArray);
	}else{
		tickerList = _createTickers(symbolArray);
		var timer = null;
		var uniqueSymbol = _getUniqueAndNoCacheSymbol();
		var uniqueSymbolTickerList = _createTickers(uniqueSymbol);
		this.tickerList = tickerList;
		_putTickersToCache(uniqueSymbolTickerList);
		_loadData(uniqueSymbol);
		timer = setInterval(function() {
			if (needResponseTimes > 0 && needResponseTimes == responsedTimes) {
				clearInterval(timer);
				_updateToReady(uniqueSymbol);
			} else if (needResponseTimes == 0) {
				clearInterval(timer);
			}
		}, 100);
	}
	
	if (callback.onSuccess && typeof(callback.onSuccess)=='function'){
		this.ready(callback.onSuccess);
	}	

	/** ************************************************************* */
	/* QSAPI.Tickers private method */
	/** ************************************************************* */

	function _putTickersToCache(uniqueSymbolTickerList) {
		for ( var i = 0 , len=uniqueSymbolTickerList.length; i < len; i++) {
			if (!QSAPI.TickersCache.tickers[uniqueSymbolTickerList[i]['symbol']]){
				QSAPI.TickersCache.add(uniqueSymbolTickerList[i]);
			}
		}
	}

	function _checkAllAreGKey(symbolArray){
		for (var i=0,len=symbolArray.length;i<len;i++){
			//"126:IBM"
			if (!/^\d+\:\w+$/.test(symbolArray[i])){
				return false;
			}
		}
		return true;
	}
	
	function _getUniqueAndNoCacheSymbol() {
		var map = {};
		var uniqueSymbol = [];
		var symbol = null;
		for ( var i = 0,len=tickerList.length; i < len; i++) {
			symbol = tickerList[i]['symbol'];
			if (!QSAPI.TickersCache.tickers[symbol]	&& !map[symbol]) {
				map[symbol] = true;
				uniqueSymbol.push(symbol);
			}
		}
		return uniqueSymbol;
	}

	function _loadData(uniqueSymbol) {
		if (uniqueSymbol.length == 0) {
			return;
		}
		var specialSymbol=[];
		for (var i=uniqueSymbol.length-1;i>=0;i--){
			if(/^\d+(\.|:)\d+(\.|:)\S+$/.test(uniqueSymbol[i])){
				specialSymbol.push(uniqueSymbol[i]);
				uniqueSymbol.splice(i,1);
			}
		}
		if (specialSymbol.length>0){
			_loadSepcialData(specialSymbol);
		}
		_loadNormalData(uniqueSymbol);
		
	}

	function _loadNormalData(uniqueSymbol){
		var symbols = new Util.StringBuffer("");
		for ( var i = 0; i < uniqueSymbol.length; i++) {
			symbols.append(uniqueSymbol[i]);			
			if ((i > 0 && (i + 1) % config.groupSymbolQuantity == 0)
					|| i == uniqueSymbol.length - 1) {
				var url = config.idService + symbols.toString() + "&cb=?";
				needResponseTimes++;
				$.getJSON(url, function(data) {
					_putDataToCacheTickers(data.Records);
				});
				symbols = new Util.StringBuffer("");
			} else {
				symbols.append(',');
			}
		}		
	}
	
	function _loadSepcialData(symbols){
		var tls,tExch,secType,tTicker;
		for (var i=0,len=symbols.length;i<len;i++){
			tls=symbols[i].split(/\.|:/);
			if(tls.length>=3){
				tExch=tls[0],secType=parseInt(tls[1],10),tTicker=tls[2];
				var ticker = QSAPI.TickersCache.tickers[symbols[i]];
				var option=secType==2?_OptionSymbolFormater(tTicker,10):{};
				//tls[0]=parseInt(tls[0],10)<126 && parseInt(tls[1],10)==2?126:tls[0];			
				$.extend(ticker, {
					'gkey' :_generateGKey(tExch,secType,tTicker,""),
					'tenforeCode' : tExch,
					'tenforeTicker' : tTicker,
					'type' : secType,
					'isReady' : true,
					'valid' : true,
					'ticker':tTicker,
					'Name':tTicker,
					'optiontype':option.optiontype,
					'rootsymbol':option.rootsymbol,
					'expirydate':option.expirydate,
					'strikeprice':option.strikeprice,
					'performanceId':symbols[i],
					'hasStaticData':true,
					"noPID":true
				});
			}
		}
	}
	
	
	function  _OptionSymbolFormater(symbol){
		var len=symbol.length;
		if(len>10){
			var denominatorMap={
				'0':1,
				'A':10,
				'B':100,
				'C':1000,
				'D':10000,
				'E':100000,
				'F':1000000,
				'G':10000000,
				'H':100000000,
				'I':1000000000
			};
			var dayMap={'A':10,'B':11,'C':12,'D':13,'E':14,'F':15,'G':16,'H':17,'I':18,'J':19,'K':20,'L':21,'M':22,'N':23,'O':24,'P':25,'Q':26,'R':27,'S':28,'T':29,'U':30,'V':31};
			var callMonthMap={'A':1,'B':2,'C':3,'D':4,'E':5,'F':6,'G':7,'H':8,'I':9,'J':10,'K':11,'L':12};
			var putMonthMap={'M':1,'N':2,'O':3,'P':4,'Q':5,'R':6,'S':7,'T':8,'U':9,'V':10,'W':11,'X':12};
			var strikePrice=parseInt(symbol.substring(len-6,len),10),
				denominator=denominatorMap[symbol.substring(len-7,len-6)],
				day=symbol.substring(len-8,len-7),
				month=symbol.substring(len-9,len-8),
				year=symbol.substring(len-10,len-9),
				optiontype='',
				rootSymbol=symbol.substring(0,len-10);
			
			day=typeof(dayMap[day])!='undefined'?dayMap[day]:'0'+day;			
			month=callMonthMap[month]?callMonthMap[month]:putMonthMap[month];
			month=month<10?'0'+month:month;
			optiontype=callMonthMap[month]?'call':'put';
			
			var currentYear=new Date().getFullYear()+'';
			year=currentYear.substring(0,3)+year;
			
			return {
				optiontype : optiontype,
				rootsymbol : rootSymbol,
				expirydate : year +'-'+ month+'-' + day,
				strikeprice : strikePrice / denominator
			};
		}		
	}

	function _checkTickerPermission(ticker) {
		if (!cdm._checkPremission(ticker)) {
			var obj = {
				'index' : ticker.index,
				'symbol' : ticker.symbol,
				'isReady' : true,
				'valid' : false
			};
			for (p in ticker) {
				if (typeof (ticker[p]) != 'function') {
					delete ticker[p];
				}
			}
			$.extend(ticker, obj);
		}
	}
	
	function _updateToReady(symbolArray) {
		for ( var i = 0,len=symbolArray.length; i <len ; i++) {
			$.extend(QSAPI.TickersCache.tickers[symbolArray[i]],{
				'isReady':true
			});
		}
	}

	function _putDataToCacheTickers(data) {
		for ( var i = 0, len = data.length; i < len; i++) {
			var queryKey = data[i]['QueryKey'];
			var ticker = QSAPI.TickersCache.tickers[queryKey];
			if(!ticker&&data[i]['Exch']){
				ticker = QSAPI.TickersCache.tickers[data[i]['Exch']+":"+queryKey];
			}
			if (ticker) {
				ticker.setData(data[i]);
				_checkTickerPermission(ticker);
				//cover old mapped ticker.
				if (ticker.getField('valid')){
					QSAPI.TickersCache.gKeyMap[ticker.gkey] = ticker;
				}
			}
		}
		responsedTimes++;
	}

	/**
	 * @param symbolArray
	 *            return boolean
	 */
	function _checkParameter(symbolArray) {
		if (symbolArray instanceof Array) {
			for ( var i = 0; i < symbolArray; i++) {
				if (typeof (symbolArray) != "String") {
					return false;
				}
			}
		} else {
			return false;
		}
		return true;
	}

	function _createTickers(symbolArray) {
		var tList = [];
		for ( var i = 0,len=symbolArray.length; i <len; i++) {
			ticker = new QSAPI.Ticker(symbolArray[i]);
			tList.push(ticker);
		}
		return tList;
	}
	
	function _getTickersByGKeys(gKeyArray){
		var tList = [];
		for ( var i = 0; i < gKeyArray.length; i++) {
			ticker = QSAPI.TickersCache.getByGKey($.trim(gKeyArray[i]));
			tList.push(ticker);
		}
		return tList;
	}

	function _generateGKey(tenforeExch,tenforeSecType,tenforeTicker,mType){//handle the compositive exhcang such as "126"
		if(tenforeExch<=126&&(tenforeSecType==1||tenforeSecType==10||tenforeSecType==8||tenforeSecType==2||mType=="XI"||mType=="ST"||mType=="FE"||mType=="FC"||mType=="FO"||(tenforeSecType==""&&(mType=="--"||mType=="")))){
			tenforeExch = 126;
		}
		return tenforeExch+":"+tenforeTicker;
	}
		
	function _throwSymbolArrayError() {
		throw new Error(
				"Error from executing QSAPI.Tickers(symbolArray).Parameter is wrong, example: new QSAPI.Tickers(['IBM','MSFT'])");
	}
};

QSAPI.Tickers.prototype = {
	ready : function(callback) {
		var tickerList = this.tickerList;
		var timer = null;
		var isAllGKey=this.isAllGKey;
		var callbackPool=this.callbackPool;
		if (typeof(callback)=='function'){			
			callbackPool.push(callback);
		}
		var allReady = _isReady();
		if (!allReady) {
			timer = setInterval(function() {
				allReady = _isReady();
				if (allReady){
					clearInterval(timer);
					_executeCallback();
				}
			}, 100);
		}else{
			_executeCallback();
		}

		function _cloneDataFromCache() {
			for ( var i = 0; i < tickerList.length; i++) {
				var srcTicker = QSAPI.TickersCache.tickers[tickerList[i].symbol];
				var t = tickerList[i];
				$.extend(t,srcTicker,{
					'index':t.index,
					'symbol':t.symbol
				});
				QSAPI.TickersCache.addGData(t);
				srcTicker=null;
			}
		}

		function _isReady() {
			if (!isAllGKey) {
				for ( var i = 0; i < tickerList.length; i++) {
					if (!QSAPI.TickersCache.tickers[tickerList[i]['symbol']]['isReady']) {
						return false;
					}
				}				
				_cloneDataFromCache();
			}
			return true;
		}
		function _executeCallback(){
			while(callbackPool.length>0){
				callbackPool[0](tickerList);
				callbackPool.splice(0,1);
			}
		}		
	},
	getTickerList : function() {
		return this.tickerList;
	}
};

/**
 * 
 * @param gKey
 *            Symbol/PerformaceId
 * @returns {QSAPI.ticker}
 */
QSAPI.Ticker = function(symbol) {		
	return {
		'index':Util.IdProducer.getId(),
		'symbol':symbol,
		'isReady':false,
		'valid':false,
		clear:function(){
	        for(var p in this){
	            if(!p || p == 'clear'){continue;}
	            this[p] = null;
	            delete this[p];
	        }			
		},
		setData : function(data) {
			var rs=data;
			_fillSType(rs);
			var tenforInfo = rs.TenforeInfo || [];
			var gkey=null;
			if(tenforInfo.length>1){
				gkey=_generateGKey(tenforInfo[1],tenforInfo.length>2?tenforInfo[2]:null,tenforInfo[0],rs.Type);		// there is tenfore type in new id service	
			}else{
				if(typeof(QS.mockExchangeCode)=="undefined"){
				    QS.mockExchangeCode = "9999";
				}			
				var ticker=$.trim(rs.Ticker);
				tenforInfo=[(ticker.length>0?ticker:rs.PID),QS.mockExchangeCode+(typeof(rs.Type)!="undefined"?"_"+rs.Type:"")];
				gkey = tenforInfo[1] + ":" + tenforInfo[0];	
			}
			
			var secType=_toStockTypeCode(rs.Type,tenforInfo[2]);
			var mType=_convertMTypeFromTenforeType(secType,rs.Type);
			var performanceId=rs.PID==""?(secType==2?tenforInfo[0]:(tenforInfo[1]+"."+secType+"."+tenforInfo[0])):rs.PID;
			var sId=rs.SID==""?(secType==2?tenforInfo[0]:(tenforInfo[1]+"."+secType+"."+tenforInfo[0])):rs.SID;		
			
			$.extend(this,{
				'mType': mType,
				'type': secType,
				'gkey': gkey,
				'tenforeCode': rs['TenforeInfo']?rs['TenforeInfo'][1]:null,
				'tenforeTicker': rs['TenforeInfo']?rs['TenforeInfo'][0]:null,
				'companyId': rs['CFID'],
				'secId': sId,
				'performanceId': performanceId,
				'msexch':QSAPI.DataManager._getMICExangeMap()[rs['Exch']]||rs['Exch'],
				'exch': rs['Exch'],
				'country': rs['Region'],
				'ISIN': rs.ISIN,
				'clientTicker': rs.ClientTicker||rs.Ticker||tenforInfo[0],
				'ticker': rs.Ticker||tenforInfo[0],
				'Name': rs.Name,
				'currency':rs['Currency']||'',
				'InitialDate': rs.InitialDate ? rs.InitialDate : "",
				'CUSIP':rs.CUSIP ? rs.CUSIP : "",
				'isReady':true,
				'valid':true
			}); 
			
			if(rs.FundData){
				$.extend(this,{
					'IMA_RDate':rs.FundData.IMA_RDate ? rs.FundData.IMA_RDate : "",
					'MStar_RDate':rs.FundData.MStar_RDate ? rs.FundData.MStar_RDate : ""
				});
			}
			if(secType==2){// options
				$.extend(this,{
					optiontype : rs.OptionType,
					rootsymbol : rs.RootSymbol,
					expirydate : _formatDateString(rs.ExpiredDate),
					strikeprice :rs.StrikePrice
				});
			}
			if (rs.Type == "FE" && rs.EtfData != null && rs.EtfData.IIV
					&& rs.EtfData.IIV.TenforeInfo != null) { // IIV for ETF ticker.
				var etf = rs.EtfData.IIV;
				var eGkey = _generateGKey(tenforInfo[1], tenforInfo.length>2?tenforInfo[2]:null,
						etf.TenforeInfo[0], rs.Type);
				if (!gData[eGkey]) {
					gData[eGkey] = {};
				}
				this.eGkey=eGkey;
				$.extend(gData[eGkey], {
					'checked' : true,
					'tenforeCode' : eGkey.split(':')[0],
					'tenforeTicker' : etf.TenforeInfo[0],
					'mType' : 'FE',
					'type' : _toStockTypeCode(etf.Type, etf.TenforeInfo[2]),
					'performanceId' : performanceId + "IIV",
					'secId' : sId,
					'MIC-CODE': etf.Exch,
					'exch' : etf.Exch,
					'ticker' : etf.Ticker,
					'country' : etf.Region,
					'Name' : etf.Name,
					'gkey' : eGkey
				});
				var ticker=new QSAPI.Ticker(etf.Ticker);
				this.etfTicker=$.extend(ticker,gData[eGkey],{ready:true,valid:true});
			}
			function _formatDateString(str){
				var t = [];
				t.push(str.slice(0,4)),t.push(str.slice(4,6)),t.push(str.slice(6,8));
				return t.join("-");
			}
			function _fillSType(rs){
				var tInfo=rs.TenforeInfo||[];
				if(!rs.Type &&tInfo.length>=3){
					if(tInfo[2]=='1'){
						rs.Type='ST';
					}else if(tInfo[2]=='10'){
						rs.Type='XI';
					}
				}
			}
			
			function _generateGKey(tenforeExch,tenforeSecType,tenforeTicker,mType){//handle the compositive exhcang such as "126"
				if(tenforeExch<=126&&(tenforeSecType==1||tenforeSecType==10||tenforeSecType==8||tenforeSecType==2||mType=="XI"||mType=="ST"||mType=="FE"||mType=="FC"||mType=="FO"||(tenforeSecType==""&&(mType=="--"||mType=="")))){
					tenforeExch = 126;
				}
				return tenforeExch+":"+tenforeTicker;
			}		
			
			function _toStockTypeCode(ty, tenforeType) {
				if (typeof (tenforeType) != "undefined" && !isNaN(tenforeType)) {
					return tenforeType;
				}
				if (ty == null)
					return 1;
				var t = ty.toUpperCase();
				if (t == "ST") { // stock
					return "1";
				} else if (t == "XI") {// Index
					return 10;
				} else if (t == "FE" || t == "FC") { // ETF
					return "1";
				} else if (t == "FO") { // FC: close fund, FO: open fund.
					return "8";
				} else {
					return "1";
				}
			}

			// according the tenfore sec type to generate mstar sectype
			function _convertMTypeFromTenforeType(secType, mType) {
				var result = null;
				if (typeof (mType) != 'undefined' && mType != null && mType != '') {
					result = mType;
				} else {
					var mType = "ST";
					switch (secType) {
					case 10:
						mType = "XI";
						break;
					case 7:
					case 8:
					case 11:
					case 16:
						mType = "Bond";
						break;
					case 13:
						mType = "Spread";
						break;
					case 2:
						mType = "Options";
						break;
					case 3:
						mType = "FU";
						break;
					case 20:
						mType = "FX";
						break;
					}
					result = mType;
				}
				return result;
			}
		},
		getField:function(field){
			return !field?this:this[field];
		},
		setField:function(field,value){
			if (field){
				this[field]=!value?null:value;
			}
		}			
	};
};




